package com.mall.framework.fliter;

import com.mall.common.result.Result;
import com.mall.common.result.ResultCode;
import com.mall.framework.config.SwaggerConfig;
import com.mall.framework.security.JwtTokenProvider;
import com.mall.framework.security.service.MallUserDetailsService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    // 白名单路径
    private static final List<String> WHITE_LIST = Arrays.asList(
        "/api/admin/login",
        "/api/admin/register",
        "/api/**"
    );

    private final JwtTokenProvider tokenProvider;
    private final MallUserDetailsService userDetailsService;
    private final ObjectMapper objectMapper = new ObjectMapper();


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws IOException, ServletException {

        // 1. 跳过白名单
        if (isWhiteListed(request.getRequestURI())) {
            chain.doFilter(request, response);
            return;
        }

        try {
            // 2. 获取并验证Token
            String jwt = getJwtFromRequest(request);
            if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
                // 3. 设置认证信息
                String username = tokenProvider.getUsernameFromToken(jwt);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);

                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities()
                );
                auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(auth);
            } else {
                // 4. 无效Token处理
                sendErrorResponse(response, "无效Token");
                return;
            }
        } catch (Exception ex) {
            // 5. 统一错误处理
            sendErrorResponse(response, "认证失败");
            return;
        }

        chain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    private boolean isWhiteListed(String uri) {
        // 检查基本白名单
        if (WHITE_LIST.stream().anyMatch(uri::startsWith)) {
            return true;
        }
        
        // 检查Swagger相关路径
        return Arrays.stream(SwaggerConfig.SWAGGER_WHITE_LIST)
            .anyMatch(pattern -> {
                if (pattern.endsWith("/**")) {
                    return uri.startsWith(pattern.substring(0, pattern.length() - 2));
                } else {
                    return uri.equals(pattern) || uri.startsWith(pattern);
                }
            });
    }


    private void sendErrorResponse(HttpServletResponse response, String msg) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=UTF-8");
        
        Result<Void> errorResult = Result.error(ResultCode.UNAUTHORIZED, msg);
        String jsonResponse = objectMapper.writeValueAsString(errorResult);
        
        response.getWriter().write(jsonResponse);
        response.getWriter().flush();
    }
}